﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.Revit.DB;

namespace YArchitech.Revit.RevitExtension
{
	public static class RevitDimensionExtension
	{
		public static void ResetDimTextPos(Dimension dimBeReset, Autodesk.Revit.DB.View view, double dViewScale = 0.0)
		{
			if (dimBeReset.Curve.GetType() != typeof(Line))
			{
				return;
			}
			DimensionType dimensionType = dimBeReset.DimensionType;
			if (dViewScale.Equals(0.0))
			{
				dViewScale = (double)view.Scale;
			}
			double num = dimensionType.GetParameter(BuiltInParameter.TEXT_WIDTH_SCALE).AsDouble();
			double num2 = dimensionType.GetParameter(BuiltInParameter.TEXT_SIZE).AsDouble() * dViewScale;
			double dCharWidth = num2 * num;
			double dOffsetExt = 0.03;
			RevitDimensionExtension.ResetTextStyleSize(dimensionType.GetParameter(BuiltInParameter.TEXT_FONT).AsString(), ref num2, ref dCharWidth);
			if (dimBeReset.Segments.Size <= 1)
			{
				RevitDimensionExtension.ResetOneSegmentDimText(dimBeReset, num2, dCharWidth, dOffsetExt, view);
				return;
			}
			if (dimBeReset.Segments.Size >= 2)
			{
				RevitDimensionExtension.ResetMultiSegmentDimText(dimBeReset, num2, dCharWidth, dOffsetExt, view);
			}
		}

		public static void ResetDimTextPos(Dimension dimBeReset)
		{
			Autodesk.Revit.DB.View activeView = dimBeReset.Document.ActiveView;
			RevitDimensionExtension.ResetDimTextPos(dimBeReset, activeView, 0.0);
		}

		public static void ResetDimTextPos(Dimension dimBeReset, double dViewScale)
		{
			Autodesk.Revit.DB.View activeView = dimBeReset.Document.ActiveView;
			RevitDimensionExtension.ResetDimTextPos(dimBeReset, activeView, dViewScale);
		}

		public static XYZ GetDimSegTextCenter(Dimension dim, DimensionSegment dimSeg)
		{
			if (dim.Curve.GetType() != typeof(Line))
			{
				return null;
			}
			Line line = dim.Curve as Line;
			return Line.CreateUnbound(dimSeg.LeaderEndPosition, line.Direction).Project(dimSeg.Origin).XYZPoint;
		}

		public static bool IsTextPosAdjustable(DimensionSegment dimSeg)
		{
			return dimSeg.IsTextPositionAdjustable();
		}

		public static void SetDimSegTextPos(DimensionSegment dimSeg, XYZ ptPos)
		{
			dimSeg.TextPosition = ptPos;
		}

		public static int GetCharNum(string sText)
		{
			if (sText.Length == 0)
			{
				return 0;
			}
			Encoding encoding = new ASCIIEncoding();
			int num = 0;
			byte[] bytes = encoding.GetBytes(sText);
			for (int i = 0; i < bytes.Length; i++)
			{
				if (bytes[i] == 63)
				{
					num += 2;
				}
				else
				{
					num++;
				}
			}
			return num;
		}

		public static double GetTextVerticalOffset(double dOffset)
		{
			return dOffset;
		}

		public static double GetJigTextVerticalOffset()
		{
			return 1.5;
		}

		private static void ResetOneSegmentDimText(Dimension dimBeReset, double dCharHeight, double dCharWidth, double dOffsetExt, Autodesk.Revit.DB.View view)
		{
			if (dimBeReset.Curve.GetType() != typeof(Line) || dimBeReset.Segments.Size > 1)
			{
				return;
			}
			Line line = dimBeReset.Curve as Line;
			Line line2 = Line.CreateUnbound(dimBeReset.LeaderEndPosition, line.Direction);
			Line line3 = Line.CreateUnbound(dimBeReset.TextPosition, line.Direction);
			XYZ vtTextUp = (line2.Project(dimBeReset.Origin).XYZPoint - dimBeReset.Origin).Normalize();
			double num = dimBeReset.GetParameter(BuiltInParameter.DIM_TOTAL_LENGTH).AsDouble();
			double num2 = dCharWidth * (double)RevitDimensionExtension.GetCharNum(dimBeReset.ValueString);
			XYZ xyz = line3.Project(dimBeReset.Origin).XYZPoint;
			XYZ xyz2 = line2.Project(dimBeReset.Origin).XYZPoint;
			XYZ xyz3 = xyz2 - xyz;
			if (num2 > num)
			{
				xyz -= line.Direction * (0.5 * (num2 + num) + dOffsetExt);
			}
			xyz2 = xyz + xyz3;
			RevitDimensionExtension.OffsetTextByElements(dimBeReset.Document, vtTextUp, num2, dCharHeight, dOffsetExt, ref xyz2, view);
			dimBeReset.TextPosition = xyz2 - xyz3;
		}

		private static void ResetOneSegmentDimText(Dimension dimBeReset, double dCharHeight, double dCharWidth, double dOffsetExt)
		{
			Autodesk.Revit.DB.View activeView = dimBeReset.Document.ActiveView;
			RevitDimensionExtension.ResetOneSegmentDimText(dimBeReset, dCharHeight, dCharWidth, dOffsetExt);
		}

		private static void ResetMultiSegmentDimText(Dimension dimBeReset, double dCharHeight, double dCharWidth, double dOffsetExt, Autodesk.Revit.DB.View view)
		{
			if (dimBeReset.Curve.GetType() != typeof(Line) || dimBeReset.Segments.Size < 2)
			{
				return;
			}
			Element dimensionType = dimBeReset.DimensionType;
			double num = (double)view.Scale;
			double num2 = dimensionType.GetParameter(BuiltInParameter.TEXT_DIST_TO_LINE).AsDouble() * num;
			DimensionSegment dimensionSegment = dimBeReset.Segments[0];
			Line line = dimBeReset.Curve as Line;
			Line line2 = Line.CreateUnbound(dimensionSegment.LeaderEndPosition, line.Direction);
			Line line3 = Line.CreateUnbound(dimensionSegment.TextPosition, line.Direction);
			XYZ xyz = (line2.Project(dimensionSegment.Origin).XYZPoint - dimensionSegment.Origin).Normalize();
			XYZ direction = line.Direction;
			List<int> list = new List<int>();
			for (int i = 0; i < dimBeReset.Segments.Size; i++)
			{
				list.Add(0);
			}
			for (int j = 0; j < dimBeReset.Segments.Size; j++)
			{
				DimensionSegment dimensionSegment2 = dimBeReset.Segments[j];
				if (dimensionSegment2.IsTextPositionAdjustable())
				{
					XYZ xyz2 = line3.Project(dimensionSegment2.Origin).XYZPoint;
					XYZ xyz3 = line2.Project(dimensionSegment2.Origin).XYZPoint;
					XYZ xyz4 = xyz3 - xyz2;
					double num3 = dimensionSegment2.Value ?? 0.0;
					double num4 = dCharWidth * (double)RevitDimensionExtension.GetCharNum(dimensionSegment2.ValueString);
					if (num4 > num3)
					{
						if (RevitDimensionExtension.IsCanMoveTextTo(dimBeReset, dCharWidth, dimensionSegment2.ValueString, j - 1))
						{
							list[j] = 1;
							xyz2 -= direction * (0.5 * (num4 + num3) + dOffsetExt);
							xyz2 += xyz * dCharHeight;
						}
						else if (RevitDimensionExtension.IsCanMoveTextTo(dimBeReset, dCharWidth, dimensionSegment2.ValueString, j + 1))
						{
							list[j] = 1;
							xyz2 += direction * (0.5 * (num4 + num3) + dOffsetExt);
							xyz2 += xyz * dCharHeight;
						}
						else if (list[j - 1] == 0)
						{
							if (RevitDimensionExtension.IsTwoDimsegTextOverlap(dimBeReset, dCharWidth, j - 1, j) || RevitDimensionExtension.IsTwoDimsegTextOverlap(dimBeReset, dCharWidth, j, j + 1))
							{
								list[j] = -1;
								xyz2 = dimensionSegment2.Origin - xyz * (num2 + 1.85 * dCharHeight);
							}
						}
						else if (RevitDimensionExtension.IsTwoDimsegTextOverlap(dimBeReset, dCharWidth, j, j + 1))
						{
							if (list[j - 1] == -1)
							{
								list[j] = 1;
								xyz2 += xyz * dCharHeight;
							}
							else
							{
								list[j] = -1;
								xyz2 = dimensionSegment2.Origin - xyz * (num2 + 1.85 * dCharHeight);
							}
						}
						else if (j > 1 && RevitDimensionExtension.IsTwoDimsegTextOverlap(dimBeReset, dCharWidth, j - 2, j))
						{
							if (list[j - 1] == -1)
							{
								list[j] = 1;
								xyz2 += xyz * dCharHeight;
							}
							else
							{
								list[j] = -1;
								xyz2 = dimensionSegment2.Origin - xyz * (num2 + 1.85 * dCharHeight);
							}
						}
					}
					if (list[j] != 0 || num4 < num3)
					{
						xyz3 = xyz2 + xyz4;
						RevitDimensionExtension.OffsetTextByElements(dimBeReset.Document, xyz, num4, dCharHeight, dOffsetExt, ref xyz3, view);
						xyz2 = xyz3 - xyz4;
					}
					dimensionSegment2.TextPosition = xyz2;
				}
			}
		}

		private static void ResetMultiSegmentDimText(Dimension dimBeReset, double dCharHeight, double dCharWidth, double dOffsetExt)
		{
			Autodesk.Revit.DB.View activeView = dimBeReset.Document.ActiveView;
			RevitDimensionExtension.ResetMultiSegmentDimText(dimBeReset, dCharHeight, dCharWidth, dOffsetExt, activeView);
		}

		private static void OffsetTextByElements(Autodesk.Revit.DB.Document doc, XYZ vtTextUp, double dTextWidth, double dTextHeight, double dOffsetExt, ref XYZ ptTextCenter, Autodesk.Revit.DB.View view)
		{
			XYZ xyz = Transform.CreateRotation(view.ViewDirection, -Math.PI*.5).OfVector(vtTextUp);
			XYZ xyz2 = ptTextCenter - xyz * 0.5 * dTextWidth - vtTextUp * 0.5 * dTextHeight;
			XYZ xyz3 = ptTextCenter + xyz * 0.5 * dTextWidth + vtTextUp * 0.5 * dTextHeight;
			try
			{
				double elevation = view.GenLevel.Elevation;
				XYZ xyz4 = new XYZ(Math.Min(xyz2.X, xyz3.X), Math.Min(xyz2.Y, xyz3.Y), elevation + 0.001);
				XYZ xyz5 = new XYZ(Math.Max(xyz2.X, xyz3.X), Math.Max(xyz2.Y, xyz3.Y), elevation + 10.0);
				BoundingBoxIntersectsFilter boundingBoxIntersectsFilter = new BoundingBoxIntersectsFilter(new Outline(xyz4, xyz5));
				foreach (Element element in new FilteredElementCollector(doc, view.Id).WherePasses(boundingBoxIntersectsFilter).ToList<Element>())
				{
					if (element.Category.Id.IntegerValue == -2001330 || element.Category.Id.IntegerValue == -2000100)
					{
						RevitDimensionExtension.OffsetTextByColumn(element, vtTextUp, dTextWidth, dTextHeight, dOffsetExt, ref ptTextCenter, view);
					}
					else if (element.Category.Id.IntegerValue == -2000011)
					{
						RevitDimensionExtension.OffsetTextByWall(element, vtTextUp, dTextWidth, dTextHeight, dOffsetExt, ref ptTextCenter);
					}
				}
			}
			catch
			{
			}
		}

		private static void OffsetTextByElements(Autodesk.Revit.DB.Document doc, XYZ vtTextUp, double dTextWidth, double dTextHeight, double dOffsetExt, ref XYZ ptTextCenter)
		{
			Autodesk.Revit.DB.View activeView = doc.ActiveView;
			RevitDimensionExtension.OffsetTextByElements(doc, vtTextUp, dTextWidth, dTextHeight, dOffsetExt, ref ptTextCenter, activeView);
		}

		private static void OffsetTextByWall(Element elemWall, XYZ vtTextUp, double dTextWidth, double dTextHeight, double dOffsetExt, ref XYZ ptTextCenter)
		{
			Wall wall = elemWall as Wall;
			if (wall == null)
			{
				return;
			}
			Curve curve = (wall.Location as LocationCurve).Curve;
			Line line = curve as Line;
			if (line == null)
			{
				return;
			}
			double length = line.Length;
			XYZ endPoint = line.GetEndPoint(0);
			XYZ endPoint2 = line.GetEndPoint(1);
			XYZ xyz = endPoint + 0.5 * (endPoint2 - endPoint);
			line.MakeUnbound();
			XYZ xyzpoint = curve.Project(ptTextCenter).XYZPoint;
			XYZ xyz2 = (ptTextCenter - xyzpoint).Normalize();
			XYZ vec = Transform.CreateRotation(XYZ.BasisZ, -Math.PI*.5).OfVector(vtTextUp);
			if (line.Direction.IsParallelTo(vtTextUp))
			{
				if (ptTextCenter.DistanceTo(xyzpoint) < 0.5 * (dTextWidth + wall.Width) && xyz.DistanceTo(xyzpoint) < 0.5 * (length + dTextHeight))
				{
					double num = 0.5 * (dTextWidth + wall.Width) - ptTextCenter.DistanceTo(xyzpoint);
					ptTextCenter += xyz2 * (num + dOffsetExt);
					return;
				}
			}
			else if (line.Direction.IsParallelTo(vec) && ptTextCenter.DistanceTo(xyzpoint) < 0.5 * (dTextHeight + wall.Width) && xyz.DistanceTo(xyzpoint) < 0.5 * (length + dTextWidth))
			{
				double num2 = 0.5 * (dTextHeight + wall.Width) - ptTextCenter.DistanceTo(xyzpoint);
				ptTextCenter += xyz2 * (num2 + dOffsetExt);
			}
		}

		private static void OffsetTextByColumn(Element elemColumn, XYZ vtTextUp, double dTextWidth, double dTextHeight, double dOffsetExt, ref XYZ ptTextCenter, Autodesk.Revit.DB.View view)
		{
			XYZ xyz = Transform.CreateRotation(XYZ.BasisZ, -Math.PI*.5).OfVector(vtTextUp);
			Line line = Line.CreateUnbound(ptTextCenter, xyz);
			Line line2 = Line.CreateUnbound(ptTextCenter, vtTextUp);
			XYZ pointAtBorder = RevitDimensionExtension.GetPointAtBorder(elemColumn.Document, elemColumn, Line.CreateUnbound(ptTextCenter, -xyz), view);
			XYZ pointAtBorder2 = RevitDimensionExtension.GetPointAtBorder(elemColumn.Document, elemColumn, line, view);
			XYZ pointAtBorder3 = RevitDimensionExtension.GetPointAtBorder(elemColumn.Document, elemColumn, Line.CreateUnbound(ptTextCenter, -vtTextUp), view);
			XYZ pointAtBorder4 = RevitDimensionExtension.GetPointAtBorder(elemColumn.Document, elemColumn, line2, view);
			if (pointAtBorder != null && pointAtBorder2 != null && pointAtBorder4 != null && pointAtBorder3 != null)
			{
				double num = pointAtBorder.DistanceTo(pointAtBorder2);
				double num2 = pointAtBorder3.DistanceTo(pointAtBorder4);
				XYZ point = (elemColumn.Location as LocationPoint).Point;
				XYZ xyzpoint = line.Project(point).XYZPoint;
				XYZ xyzpoint2 = line2.Project(point).XYZPoint;
				if (ptTextCenter.DistanceTo(xyzpoint) < 0.5 * (dTextWidth + num) && ptTextCenter.DistanceTo(xyzpoint2) < 0.5 * (dTextHeight + num2))
				{
					if (ptTextCenter.DistanceTo(xyzpoint) > ptTextCenter.DistanceTo(xyzpoint2))
					{
						XYZ xyz2 = (ptTextCenter - xyzpoint).Normalize();
						double num3 = 0.5 * (dTextWidth + num) - ptTextCenter.DistanceTo(xyzpoint);
						ptTextCenter += xyz2 * (num3 + dOffsetExt);
						return;
					}
					XYZ xyz3 = (ptTextCenter - xyzpoint2).Normalize();
					double num4 = 0.5 * (dTextHeight + num2) - ptTextCenter.DistanceTo(xyzpoint2);
					ptTextCenter += xyz3 * (num4 + dOffsetExt);
				}
			}
		}

		private static void OffsetTextByColumn(Element elemColumn, XYZ vtTextUp, double dTextWidth, double dTextHeight, double dOffsetExt, ref XYZ ptTextCenter)
		{
			RevitDimensionExtension.OffsetTextByColumn(elemColumn, vtTextUp, dTextWidth, dTextHeight, dOffsetExt, ref ptTextCenter, elemColumn.Document.ActiveView);
		}

		private static XYZ GetPointAtBorder(Autodesk.Revit.DB.Document doc, Element elem, Line lineOffset, Autodesk.Revit.DB.View view)
		{
			XYZ xyz = null;
			foreach (object obj in RevitDimensionExtension.GetElementFaces(doc, elem, view))
			{
				PlanarFace planarFace = ((Face)obj) as PlanarFace;
				if (!(null == planarFace) && planarFace.FaceNormal.IsParallelTo(lineOffset.Direction))
				{
					XYZ xyzpoint = lineOffset.Project(planarFace.Origin).XYZPoint;
					if (xyz == null)
					{
						xyz = xyzpoint;
					}
					else if ((xyzpoint - xyz).Normalize().IsSameDirectionTo(lineOffset.Direction))
					{
						xyz = xyzpoint;
					}
				}
			}
			return xyz;
		}

		private static XYZ GetPointAtBorder(Autodesk.Revit.DB.Document doc, Element elem, Line lineOffset)
		{
			return RevitDimensionExtension.GetPointAtBorder(doc, elem, lineOffset, doc.ActiveView);
		}

		private static FaceArray GetElementFaces(Autodesk.Revit.DB.Document doc, Element e, Autodesk.Revit.DB.View view)
		{
			FaceArray faceArray = new FaceArray();
			GeometryElement geometryElement = e.get_Geometry(new Options
			{
				ComputeReferences = true,
				View = view
			});
			List<GeometryObject> list = new List<GeometryObject>();
			foreach (GeometryObject item in geometryElement)
			{
				list.Add(item);
			}
			for (int i = 0; i < list.Count; i++)
			{
				GeometryObject geometryObject = list[i];
				if (geometryObject is GeometryInstance)
				{
					RevitDimensionExtension.GetFaces(geometryObject, ref faceArray);
				}
				else if (geometryObject is Solid)
				{
					foreach (object obj in (geometryObject as Solid).Faces)
					{
						Face face = (Face)obj;
						faceArray.Append(face);
					}
				}
			}
			return faceArray;
		}

		private static FaceArray GetElementFaces(Autodesk.Revit.DB.Document doc, Element e)
		{
			Autodesk.Revit.DB.View activeView = doc.ActiveView;
			return RevitDimensionExtension.GetElementFaces(doc, e, activeView);
		}

		private static void GetFaces(object geoObject, ref FaceArray arFaces)
		{
			GeometryInstance geometryInstance = geoObject as GeometryInstance;
			List<GeometryObject> list = new List<GeometryObject>();
			foreach (GeometryObject item in geometryInstance.GetInstanceGeometry())
			{
				list.Add(item);
			}
			foreach (object obj in list)
			{
				if (obj is GeometryInstance)
				{
					RevitDimensionExtension.GetFaces(obj, ref arFaces);
				}
				else if (obj is Solid)
				{
					foreach (object obj2 in (obj as Solid).Faces)
					{
						Face face = (Face)obj2;
						arFaces.Append(face);
					}
				}
			}
		}

		private static void ResetTextStyleSize(string strTextStyle, ref double dCharHeight, ref double dCharWidth)
		{
			if (strTextStyle == "微软雅黑")
			{
				dCharHeight *= 1.03;
				dCharWidth *= 0.7;
				return;
			}
			if (strTextStyle == "长仿宋体")
			{
				dCharHeight *= 0.8;
				dCharWidth *= 0.55;
				return;
			}
			if (strTextStyle == "新宋体" || strTextStyle == "宋体" || strTextStyle == "仿宋" || strTextStyle == "黑体")
			{
				dCharHeight *= 0.85;
				dCharWidth *= 0.55;
				return;
			}
			dCharHeight *= 1.0;
			dCharWidth *= 0.8;
		}

		private static bool IsTwoDimsegTextOverlap(Dimension dim, double dCharWidth, int nSegIndex1, int nSegIndex2)
		{
			if (nSegIndex1 < 0 || nSegIndex1 >= dim.Segments.Size)
			{
				return false;
			}
			if (nSegIndex2 < 0 || nSegIndex2 >= dim.Segments.Size)
			{
				return false;
			}
			DimensionSegment dimensionSegment = dim.Segments[nSegIndex1];
			double num = dCharWidth * (double)RevitDimensionExtension.GetCharNum(dimensionSegment.ValueString);
			DimensionSegment dimensionSegment2 = dim.Segments[nSegIndex2];
			double num2 = dCharWidth * (double)RevitDimensionExtension.GetCharNum(dimensionSegment.ValueString);
			return (num + num2) / 2.0 > dimensionSegment.Origin.DistanceTo(dimensionSegment2.Origin);
		}

		private static bool IsCanMoveTextTo(Dimension dim, double dCharWidth, string strMoveText, int nSegIndexTo)
		{
			if (nSegIndexTo < 0 || nSegIndexTo >= dim.Segments.Size)
			{
				return true;
			}
			DimensionSegment dimensionSegment = dim.Segments[nSegIndexTo];
			double num = dimensionSegment.Value ?? 0.0;
			double num2 = dCharWidth * (double)RevitDimensionExtension.GetCharNum(dimensionSegment.ValueString);
			double num3 = 0.5 * (num - num2);
			double num4 = dCharWidth * (double)RevitDimensionExtension.GetCharNum(strMoveText);
			return num3 - num4 > dCharWidth;
		}
	}
}
